/*!
 * # Semantic UI 1.11.4 - Modal
 * http://github.com/semantic-org/semantic-ui/
 *
 *
 * Copyright 2014 Contributors
 * Released under the MIT license
 * http://opensource.org/licenses/MIT
 *
 */

;
(function ($, window, document, undefined) {

  "use strict";

  $.fn.modal = function (parameters) {
    var
      $allModules = $(this),
      $window = $(window),
      $document = $(document),
      $body = $('body'),

      moduleSelector = $allModules.selector || '',

      time = new Date().getTime(),
      performance = [],

      query = arguments[0],
      methodInvoked = (typeof query == 'string'),
      queryArguments = [].slice.call(arguments, 1),

      requestAnimationFrame = window.requestAnimationFrame
        || window.mozRequestAnimationFrame
        || window.webkitRequestAnimationFrame
        || window.msRequestAnimationFrame
        || function (callback) {
          setTimeout(callback, 0);
        },

      returnedValue
      ;

    $allModules
      .each(function () {
        var
          settings = ( $.isPlainObject(parameters) )
            ? $.extend(true, {}, $.fn.modal.settings, parameters)
            : $.extend({}, $.fn.modal.settings),

          selector = settings.selector,
          className = settings.className,
          namespace = settings.namespace,
          error = settings.error,

          eventNamespace = '.' + namespace,
          moduleNamespace = 'module-' + namespace,

          $module = $(this),
          $context = $(settings.context),
          $close = $module.find(selector.close),

          $allModals,
          $otherModals,
          $focusedElement,
          $dimmable,
          $dimmer,

          element = this,
          instance = $module.data(moduleNamespace),

          elementNamespace,
          id,
          observer,
          module
          ;
        module = {

          initialize: function () {
            module.verbose('Initializing dimmer', $context);

            module.create.id();
            module.create.dimmer();
            module.refreshModals();

            module.verbose('Attaching close events', $close);
            module.bind.events();
            module.observeChanges();
            module.instantiate();
          },

          instantiate: function () {
            module.verbose('Storing instance of modal');
            instance = module;
            $module
              .data(moduleNamespace, instance)
            ;
          },

          create: {
            dimmer: function () {
              var
                defaultSettings = {
                  debug: settings.debug,
                  dimmerName: 'modals',
                  duration: {
                    show: settings.duration,
                    hide: settings.duration
                  }
                },
                dimmerSettings = $.extend(true, defaultSettings, settings.dimmerSettings)
                ;
              if ($.fn.dimmer === undefined) {
                module.error(error.dimmer);
                return;
            }
              module.debug('Creating dimmer with settings', dimmerSettings);
              $dimmable = $context.dimmer(dimmerSettings);
              if (settings.detachable) {
                module.verbose('Modal is detachable, moving content into dimmer');
                $dimmable.dimmer('add content', $module);
              }
              $dimmer = $dimmable.dimmer('get dimmer');
          },
            id: function () {
              id = (Math.random().toString(16) + '000000000').substr(2, 8);
              elementNamespace = '.' + id;
              module.verbose('Creating unique id for element', id);
            }
          },

          destroy: function () {
            module.verbose('Destroying previous modal');
            $module
              .removeData(moduleNamespace)
              .off(eventNamespace)
            ;
            $window.off(elementNamespace);
            $close.off(eventNamespace);
            $context.dimmer('destroy');
          },

          observeChanges: function () {
            if ('MutationObserver' in window) {
              observer = new MutationObserver(function (mutations) {
                module.debug('DOM tree modified, refreshing');
                module.refresh();
              });
              observer.observe(element, {
                childList: true,
                subtree: true
              });
              module.debug('Setting up mutation observer', observer);
            }
          },

          refresh: function () {
            module.remove.scrolling();
            module.cacheSizes();
            module.set.screenHeight();
            module.set.type();
            module.set.position();
          },

          refreshModals: function () {
            $otherModals = $module.siblings(selector.modal);
            $allModals = $otherModals.add($module);
          },

          attachEvents: function (selector, event) {
            var
              $toggle = $(selector)
              ;
            event = $.isFunction(module[event])
              ? module[event]
              : module.toggle
            ;
            if ($toggle.length > 0) {
              module.debug('Attaching modal events to element', selector, event);
              $toggle
                .off(eventNamespace)
                .on('click' + eventNamespace, event)
            ;
            }
            else {
              module.error(error.notFound, selector);
            }
          },

          bind: {
            events: function () {
              $close.on('click' + eventNamespace, module.event.close);
              $window.on('resize' + elementNamespace, module.event.resize);
            }
          },

          get: {
            id: function () {
              return (Math.random().toString(16) + '000000000').substr(2, 8);
            }
          },

          event: {
            close: function () {
              module.verbose('Closing element pressed');
              if ($(this).is(selector.approve)) {
                if (settings.onApprove.call(element) !== false) {
                  module.hide();
              }
              else {
                  module.verbose('Approve callback returned false cancelling hide');
                }
              }
              else if ($(this).is(selector.deny)) {
                if (settings.onDeny.call(element) !== false) {
                module.hide();
              }
                else {
                  module.verbose('Deny callback returned false cancelling hide');
              }
              }
              else {
                module.hide();
              }
            },
            click: function (event) {
              if ($(event.target).closest($module).length === 0) {
                module.debug('Dimmer clicked, hiding all modals');
                if (module.is.active()) {
                  module.remove.clickaway();
                  if (settings.allowMultiple) {
                  module.hide();
                }
                else {
                    module.hideAll();
                }
              }
            }
          },
            debounce: function (method, delay) {
              clearTimeout(module.timer);
              module.timer = setTimeout(method, delay);
          },
            keyboard: function (event) {
              var
                keyCode = event.which,
                escapeKey = 27
            ;
              if (keyCode == escapeKey) {
                if (settings.closable) {
                  module.debug('Escape key pressed hiding modal');
                  module.hide();
                }
                else {
                  module.debug('Escape key pressed, but closable is set to false');
                }
                event.preventDefault();
            }
          },
            resize: function () {
              if ($dimmable.dimmer('is active')) {
                requestAnimationFrame(module.refresh);
            }
            }
          },

          toggle: function () {
            if (module.is.active() || module.is.animating()) {
              module.hide();
            }
            else {
              module.show();
            }
          },

          show: function (callback) {
            callback = $.isFunction(callback)
              ? callback
              : function () {
            }
            ;
            module.refreshModals();
            module.showModal(callback);
          },

          hide: function (callback) {
            callback = $.isFunction(callback)
              ? callback
              : function () {
            }
            ;
            module.refreshModals();
            module.hideModal(callback);
          },

          showModal: function (callback) {
            callback = $.isFunction(callback)
              ? callback
              : function () {
            }
            ;
            if (module.is.animating() || !module.is.active()) {

              module.showDimmer();
              module.cacheSizes();
              module.set.position();
              module.set.screenHeight();
              module.set.type();
              module.set.clickaway();

              if (!settings.allowMultiple && $otherModals.filter('.' + className.active).length > 0) {
                module.debug('Other modals visible, queueing show animation');
                module.hideOthers(module.showModal);
              }
              else {
                settings.onShow.call(element);
                if (settings.transition && $.fn.transition !== undefined && $module.transition('is supported')) {
                  module.debug('Showing modal with css animations');
                  $module
                    .transition({
                      debug: settings.debug,
                      animation: settings.transition + ' in',
                      queue: settings.queue,
                      duration: settings.duration,
                      useFailSafe: true,
                      onComplete: function () {
                      settings.onVisible.apply(element);
                      module.add.keyboardShortcuts();
                      module.save.focus();
                      module.set.active();
                        module.set.autofocus();
                      callback();
                    }
                  })
                ;
              }
              else {
                  module.debug('Showing modal with javascript');
                $module
                  .fadeIn(settings.duration, settings.easing, function () {
                    settings.onVisible.apply(element);
                    module.add.keyboardShortcuts();
                    module.save.focus();
                    module.set.active();
                    callback();
                  })
                ;
              }
            }
            }
            else {
              module.debug('Modal is already visible');
            }
          },

          hideModal: function (callback, keepDimmed) {
            callback = $.isFunction(callback)
              ? callback
              : function () {
            }
            ;
            module.debug('Hiding modal');
            settings.onHide.call(element);

            if (module.is.animating() || module.is.active()) {
              if (settings.transition && $.fn.transition !== undefined && $module.transition('is supported')) {
                module.remove.active();
                $module
                  .transition({
                    debug: settings.debug,
                    animation: settings.transition + ' out',
                    queue: settings.queue,
                    duration: settings.duration,
                    useFailSafe: true,
                    onStart: function () {
                      if (!module.othersActive() && !keepDimmed) {
                        module.hideDimmer();
                      }
                      module.remove.keyboardShortcuts();
                    },
                    onComplete: function () {
                      settings.onHidden.call(element);
                      module.restore.focus();
                      callback();
                    }
                  })
                ;
            }
            else {
                module.remove.active();
                if (!module.othersActive()) {
                  module.hideDimmer();
                }
                module.remove.keyboardShortcuts();
                $module
                  .fadeOut(settings.duration, settings.easing, function () {
                    settings.onHidden.call(element);
                    module.restore.focus();
                    callback();
                  })
              ;
            }
            }
          },

          showDimmer: function () {
            if ($dimmable.dimmer('is animating') || !$dimmable.dimmer('is active')) {
              module.debug('Showing dimmer');
              $dimmable.dimmer('show');
            }
            else {
              module.debug('Dimmer already visible');
            }
          },

          hideDimmer: function () {
            if ($dimmable.dimmer('is animating') || ($dimmable.dimmer('is active'))) {
              $dimmable.dimmer('hide', function () {
                if (settings.transition && $.fn.transition !== undefined && $module.transition('is supported')) {
                  module.remove.clickaway();
                  module.remove.screenHeight();
                }
              });
            }
            else {
              module.debug('Dimmer is not visible cannot hide');
              return;
            }
          },

          hideAll: function (callback) {
            var
              $visibleModals = $allModals.filter(':visible')
              ;
            callback = $.isFunction(callback)
              ? callback
              : function () {
            }
            ;
            if ($visibleModals.length > 0) {
              module.debug('Hiding all visible modals');
              module.hideDimmer();
              $visibleModals
                .modal('hide modal', callback)
            ;
            }
          },

          hideOthers: function (callback) {
            var
              $visibleModals = $otherModals.filter(':visible')
              ;
            callback = $.isFunction(callback)
              ? callback
              : function () {
            }
            ;
            if ($visibleModals.length > 0) {
              module.debug('Hiding other modals', $otherModals);
              $visibleModals
                .modal('hide modal', callback, true)
            ;
            }
          },

          othersActive: function () {
            return ($otherModals.filter('.' + className.active).length > 0);
          },

          add: {
            keyboardShortcuts: function () {
              module.verbose('Adding keyboard shortcuts');
              $document
                .on('keyup' + eventNamespace, module.event.keyboard)
              ;
            }
          },

          save: {
            focus: function () {
              $focusedElement = $(document.activeElement).blur();
            }
          },

          restore: {
            focus: function () {
              if ($focusedElement && $focusedElement.length > 0) {
                $focusedElement.focus();
            }
            }
          },

          remove: {
            active: function () {
              $module.removeClass(className.active);
          },
            clickaway: function () {
              if (settings.closable) {
                $dimmer
                  .off('click' + elementNamespace)
              ;
            }
          },
            screenHeight: function () {
              if (module.cache.height > module.cache.pageHeight) {
                module.debug('Removing page height');
                $body
                  .css('height', '')
                ;
            }
          },
            keyboardShortcuts: function () {
              module.verbose('Removing keyboard shortcuts');
              $document
                .off('keyup' + eventNamespace)
              ;
            },
            scrolling: function () {
              $dimmable.removeClass(className.scrolling);
              $module.removeClass(className.scrolling);
            }
          },

          cacheSizes: function () {
            var
              modalHeight = $module.outerHeight()
              ;
            if (module.cache === undefined || modalHeight !== 0) {
              module.cache = {
                pageHeight: $(document).outerHeight(),
                height: modalHeight + settings.offset,
                contextHeight: (settings.context == 'body')
                  ? $(window).height()
                  : $dimmable.height()
              };
            }
            module.debug('Caching modal and container sizes', module.cache);
          },

          can: {
            fit: function () {
              return ( ( module.cache.height + (settings.padding * 2) ) < module.cache.contextHeight);
            }
          },

          is: {
            active: function () {
              return $module.hasClass(className.active);
          },
            animating: function () {
              return $module.transition('is supported')
                ? $module.transition('is animating')
                : $module.is(':visible')
                ;
            },
            scrolling: function () {
              return $dimmable.hasClass(className.scrolling);
            },
            modernBrowser: function () {
              // appName for IE11 reports 'Netscape' can no longer use
              return !(window.ActiveXObject || "ActiveXObject" in window);
            }
          },

          set: {
            autofocus: function () {
              if (settings.autofocus) {
                var
                  $inputs = $module.find(':input:visible'),
                  $autofocus = $inputs.filter('[autofocus]'),
                  $input = ($autofocus.length > 0)
                    ? $autofocus
                    : $inputs
              ;
                $input.first().focus();
            }
          },
            clickaway: function () {
              if (settings.closable) {
                $dimmer
                  .on('click' + elementNamespace, module.event.click)
              ;
            }
          },
            screenHeight: function () {
              if (module.can.fit()) {
                $body.css('height', '');
            }
              else {
                module.debug('Modal is taller than page content, resizing page height');
                $body
                  .css('height', module.cache.height + (settings.padding / 2))
                ;
            }
          },
            active: function () {
              $module.addClass(className.active);
            },
            scrolling: function () {
              $dimmable.addClass(className.scrolling);
              $module.addClass(className.scrolling);
            },
            type: function () {
              if (module.can.fit()) {
                module.verbose('Modal fits on screen');
                if (!module.othersActive) {
                  module.remove.scrolling();
              }
            }
              else {
                module.verbose('Modal cannot fit on screen setting to scrolling');
                module.set.scrolling();
              }
          },
            position: function () {
              module.verbose('Centering modal on page', module.cache);
              if (module.can.fit()) {
                $module
                  .css({
                    top: '',
                    marginTop: -(module.cache.height / 2)
                  })
                ;
            }
            else {
                $module
                  .css({
                    marginTop: '',
                    top: $document.scrollTop()
                  })
                ;
            }
            }
          },

          setting: function (name, value) {
            module.debug('Changing setting', name, value);
            if ($.isPlainObject(name)) {
              $.extend(true, settings, name);
            }
            else if (value !== undefined) {
              settings[name] = value;
            }
            else {
              return settings[name];
            }
          },
          internal: function (name, value) {
            if ($.isPlainObject(name)) {
              $.extend(true, module, name);
            }
            else if (value !== undefined) {
              module[name] = value;
            }
            else {
              return module[name];
            }
          },
          debug: function () {
            if (settings.debug) {
              if (settings.performance) {
                module.performance.log(arguments);
            }
              else {
                module.debug = Function.prototype.bind.call(console.info, console, settings.name + ':');
                module.debug.apply(console, arguments);
            }
            }
          },
          verbose: function () {
            if (settings.verbose && settings.debug) {
              if (settings.performance) {
                module.performance.log(arguments);
              }
            else {
                module.verbose = Function.prototype.bind.call(console.info, console, settings.name + ':');
                module.verbose.apply(console, arguments);
            }
            }
          },
          error: function () {
            module.error = Function.prototype.bind.call(console.error, console, settings.name + ':');
            module.error.apply(console, arguments);
          },
          performance: {
            log: function (message) {
              var
                currentTime,
                executionTime,
                previousTime
                ;
              if (settings.performance) {
                currentTime = new Date().getTime();
                previousTime = time || currentTime;
                executionTime = currentTime - previousTime;
                time = currentTime;
                performance.push({
                  'Name': message[0],
                  'Arguments': [].slice.call(message, 1) || '',
                  'Element': element,
                  'Execution Time': executionTime
                });
              }
              clearTimeout(module.performance.timer);
              module.performance.timer = setTimeout(module.performance.display, 100);
          },
            display: function () {
              var
                title = settings.name + ':',
                totalTime = 0
                ;
              time = false;
              clearTimeout(module.performance.timer);
              $.each(performance, function (index, data) {
                totalTime += data['Execution Time'];
              });
              title += ' ' + totalTime + 'ms';
              if (moduleSelector) {
                title += ' \'' + moduleSelector + '\'';
              }
              if ((console.group !== undefined || console.table !== undefined) && performance.length > 0) {
                console.groupCollapsed(title);
                if (console.table) {
                  console.table(performance);
              }
              else {
                  $.each(performance, function (index, data) {
                    console.log(data['Name'] + ': ' + data['Execution Time'] + 'ms');
                  });
              }
                console.groupEnd();
            }
              performance = [];
            }
          },
          invoke: function (query, passedArguments, context) {
            var
              object = instance,
              maxDepth,
              found,
              response
              ;
            passedArguments = passedArguments || queryArguments;
            context = element || context;
            if (typeof query == 'string' && object !== undefined) {
              query = query.split(/[\. ]/);
              maxDepth = query.length - 1;
              $.each(query, function (depth, value) {
                var camelCaseValue = (depth != maxDepth)
                    ? value + query[depth + 1].charAt(0).toUpperCase() + query[depth + 1].slice(1)
                    : query
                  ;
                if ($.isPlainObject(object[camelCaseValue]) && (depth != maxDepth)) {
                  object = object[camelCaseValue];
              }
                else if (object[camelCaseValue] !== undefined) {
                  found = object[camelCaseValue];
                  return false;
              }
                else if ($.isPlainObject(object[value]) && (depth != maxDepth)) {
                  object = object[value];
              }
                else if (object[value] !== undefined) {
                  found = object[value];
                  return false;
              }
                else {
                  return false;
              }
              });
          }
            if ($.isFunction(found)) {
              response = found.apply(context, passedArguments);
          }
            else if (found !== undefined) {
              response = found;
          }
            if ($.isArray(returnedValue)) {
              returnedValue.push(response);
            }
            else if (returnedValue !== undefined) {
              returnedValue = [returnedValue, response];
            }
            else if (response !== undefined) {
              returnedValue = response;
            }
            return found;
          }
        };

        if (methodInvoked) {
          if (instance === undefined) {
          module.initialize();
        }
          module.invoke(query);
        }
        else {
          if (instance !== undefined) {
            instance.invoke('destroy');
          }
          module.initialize();
        }
      })
    ;

    return (returnedValue !== undefined)
      ? returnedValue
      : this
      ;
  };

  $.fn.modal.settings = {

    name: 'Modal',
    namespace: 'modal',

    debug: false,
    verbose: true,
    performance: true,

    allowMultiple: false,
    detachable: true,
    closable: true,
    autofocus: true,

    dimmerSettings: {
      closable: false,
      useCSS: true
    },

    context: 'body',

    queue: false,
    duration: 500,
    easing: 'easeOutExpo',
    offset: 0,
    transition: 'scale',

    padding: 50,

    onShow: function () {
    },
    onHide: function () {
    },

    onVisible: function () {
    },
    onHidden: function () {
    },

    onApprove: function () {
      return true;
    },
    onDeny: function () {
      return true;
    },

    selector: {
      close: '.close, .actions .button',
      approve: '.actions .positive, .actions .approve, .actions .ok',
      deny: '.actions .negative, .actions .deny, .actions .cancel',
      modal: '.ui.modal'
    },
    error: {
      dimmer: 'UI Dimmer, a required component is not included in this page',
      method: 'The method you called is not defined.',
      notFound: 'The element you specified could not be found'
    },
    className: {
      active: 'active',
      animating: 'animating',
      scrolling: 'scrolling'
    }
  };


})(jQuery, window, document);
